﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using TraceView.Shared;

namespace TraceView.UI
{
    [MarkupExtensionReturnType(typeof (IValueConverter))]
    public abstract class BaseConverter<TInput, TOutput> : MarkupExtension, IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //if (!typeof(TOutput).IsAssignableFrom(targetType))
            ////if (targetType != typeof (TOutput))
            //    throw new InvalidOperationException("Wrong converter output type");
            //if (value != null && !typeof(TInput).IsAssignableFrom(value.GetType()))
            ////if (value != null && value.GetType() != typeof (TInput))
            //    throw new InvalidOperationException("Wrong converter input type");
            return InternalConvert((TInput) value, targetType, parameter, culture);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }

        #endregion

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }

        protected abstract TOutput InternalConvert(TInput value, Type targetType, object parameter, CultureInfo culture);
    }

    [MarkupExtensionReturnType(typeof (IMultiValueConverter))]
    public abstract class BaseMultiValueConverter<TOutput> : MarkupExtension, IMultiValueConverter
    {
        #region IMultiValueConverter Members

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return InternalConvert(values, targetType, parameter, culture);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }

        #endregion

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }

        protected abstract TOutput InternalConvert(object[] values, Type targetType, object parameter,
                                                   CultureInfo culture);
    }

    public abstract class BooleanConverter<T> : BaseConverter<bool, T>
    {
        public T TrueMapsTo { get; set; }
        public T FalseMapsTo { get; set; }

        protected override T InternalConvert(bool value, Type targetType, object parameter, CultureInfo culture)
        {
            //if (TrueMapsTo.GetType() != targetType)
            //    throw new ArgumentException("TrueMapsTo should be of type " + targetType.FullName);
            //if (FalseMapsTo.GetType() != targetType)
            //    throw new ArgumentException("FalseMapsTo should be of type " + targetType.FullName);
            return value ? TrueMapsTo : FalseMapsTo;
        }
    }

    public sealed class BoolToVisibilityConverter : BooleanConverter<Visibility>
    {
    }

    public sealed class CategoryStringBuilder : BaseConverter<StringCollection, string>
    {
        protected override string InternalConvert(StringCollection value, Type targetType, object parameter,
                                                  CultureInfo culture)
        {
            if (value == null || value.Count == 0)
                return string.Empty;

            var sb = new StringBuilder();
            foreach (string item in value)
                sb.Append(item).Append("   ");
            return sb.ToString();
        }
    }

    public abstract class SeverityConverter<T> : BaseConverter<Severity, T>
    {
        protected static readonly Dictionary<Severity, Tuple<string, string, string, string>> Names =
            new Dictionary<Severity, Tuple<string, string, string, string>>(5)
                {
                    {
                        Severity.Error,
                        new Tuple<string, string, string, string>("MetroRed", "ErrorItemSelectedBrush", "MetroWhite",
                                                                  "W")
                        },
                    {
                        Severity.Warning,
                        new Tuple<string, string, string, string>("MetroYellow", "WarningItemSelectedBrush",
                                                                  "WarningSelectedTextBrush", "B")
                        },
                    {
                        Severity.Information,
                        new Tuple<string, string, string, string>("MetroBlue", "InformationItemSelectedBrush",
                                                                  "MetroWhite", "W")
                        },
                    {
                        Severity.Critical,
                        new Tuple<string, string, string, string>("MetroRed", "ErrorItemSelectedBrush", "MetroWhite",
                                                                  "W")
                        },
                    {
                        Severity.Verbose,
                        new Tuple<string, string, string, string>("MetroBlue", "InformationItemSelectedBrush",
                                                                  "MetroWhite", "W")
                        },
                };
    }

    public sealed class SeverityToColorConverter : SeverityConverter<SolidColorBrush>
    {
        protected override SolidColorBrush InternalConvert(Severity value, Type targetType, object parameter,
                                                           CultureInfo culture)
        {
            Tuple<string, string, string, string> resourceNames;
            if (!Names.TryGetValue(value, out resourceNames))
                return new SolidColorBrush(Colors.Black);
            return Application.Current.TryFindResource(resourceNames.Item1) as SolidColorBrush ??
                   new SolidColorBrush(Colors.Black);
        }
    }

    public sealed class SeverityToSelectedItemBrushConverter : SeverityConverter<LinearGradientBrush>
    {
        protected override LinearGradientBrush InternalConvert(Severity value, Type targetType, object parameter,
                                                               CultureInfo culture)
        {
            Tuple<string, string, string, string> resourceNames;
            return Names.TryGetValue(value, out resourceNames)
                       ? Application.Current.TryFindResource(resourceNames.Item2) as LinearGradientBrush
                       : null;
        }
    }

    public sealed class SeverityToSelectedItemTextBrush : SeverityConverter<SolidColorBrush>
    {
        protected override SolidColorBrush InternalConvert(Severity value, Type targetType, object parameter,
                                                           CultureInfo culture)
        {
            Tuple<string, string, string, string> resourceNames;
            if (!Names.TryGetValue(value, out resourceNames))
                return Application.Current.TryFindResource("MetroWhite") as SolidColorBrush;
            return Application.Current.TryFindResource(resourceNames.Item3) as SolidColorBrush ??
                   Application.Current.TryFindResource("MetroWhite") as SolidColorBrush;
        }
    }

    public sealed class SeverityToImageConverter : SeverityConverter<BitmapImage>
    {
        public string Name { get; set; }

        protected override BitmapImage InternalConvert(Severity value, Type targetType, object parameter,
                                                       CultureInfo culture)
        {
            Tuple<string, string, string, string> resourceNames;
            if (!Names.TryGetValue(value, out resourceNames))
                return null;
            return Application.Current.TryFindResource(Name + resourceNames.Item4) as BitmapImage;
        }
    }

    public sealed class LineTrimConverter : BaseConverter<string, string>
    {
        public int MaxLineCount { get; set; }

        protected override string InternalConvert(string value, Type targetType, object parameter, CultureInfo culture)
        {
            if (string.IsNullOrWhiteSpace(value))
                return value;

            string[] parts = value.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
            return string.Join(Environment.NewLine, parts, 0, Math.Min(MaxLineCount, parts.Length));
        }
    }

    public sealed class CountToVisibilityConverter : BaseConverter<int, Visibility>
    {
        public Visibility TrueMapsTo { get; set; }
        public Visibility FalseMapsTo { get; set; }
        protected override Visibility InternalConvert(int value, Type targetType, object parameter, CultureInfo culture)
        {
            int number;
            if (int.TryParse(parameter.ToString(), out number))
                return value - number == 0 ? TrueMapsTo : FalseMapsTo;
            return Visibility.Collapsed;
        }
    }

    public sealed class MultiVisibilityAnyConverter : BaseMultiValueConverter<Visibility>
    {
        protected override Visibility InternalConvert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.OfType<Visibility>().Any(value => value == Visibility.Visible) ? Visibility.Visible : Visibility.Collapsed;
        }
    }

    public sealed class MultiValueLineTrimConverter : BaseMultiValueConverter<string>
    {
        protected override string InternalConvert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length == 2)
            {
                string message = values[0].ToString();
                int count = message.Length;                
                if (values[1] is int)
                    count = Math.Min((int)values[1], count);
                return message.Substring(0, count);
            }
            return null;
        }
    }

    public sealed class DummyConverter : BaseConverter<object,object>
    {
        protected override object InternalConvert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }

    public sealed class StringToBoolConverter : BaseConverter<string,bool>        
    {
        public bool TrueMapsTo { get; set; }
        public bool FalseMapsTo { get; set; }
        protected override bool InternalConvert(string value, Type targetType, object parameter, CultureInfo culture)
        {
            return string.IsNullOrEmpty(value) ? TrueMapsTo : FalseMapsTo;
        }
    }
}